use snowflake_multi_threaded::SnowFlakeId;
use crate::base::errors::RBlogError;
use crate::models::user::User;

use aes::Aes256;
use block_modes::{BlockMode, Cbc};
use block_modes::block_padding::Pkcs7;
use rand::seq::SliceRandom;
use rbatis::Rbatis;

type AesCbc = Cbc<Aes256, Pkcs7>;

const BASE_STR: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

// 采用雪花算法
fn get_snow_id() -> String {
    let mut b = SnowFlakeId::new(1, 1);
    let raw_id = b.generate_id().unwrap() as u64;
    format!("{:?}", raw_id)
}


pub fn generate_user_id() -> String{
    get_snow_id()
}


pub fn gen_ascii_chars(size: usize) -> String {
    let mut rng = &mut rand::thread_rng();
    String::from_utf8(
        BASE_STR.as_bytes()
            .choose_multiple(&mut rng, size)
            .cloned()
            .collect()
    ).unwrap()
}

pub fn encrypt(key: &str, data: &str) -> Result<String, RBlogError> {
    let iv_str = gen_ascii_chars(16);
    let iv = iv_str.as_bytes();
    let cipher = AesCbc::new_from_slices(key.as_bytes(), iv).unwrap();
    let ciphertext = cipher.encrypt_vec(data.as_bytes());
    let mut buffer = bytebuffer::ByteBuffer::from_bytes(iv);
    buffer.write_bytes(&ciphertext);
    Ok(
        base64::encode(buffer.to_bytes())
    )
}

pub fn decrypt(key: &str, data: &str) -> Result<String, RBlogError> {
    let bytes = base64::decode(data).unwrap();
    let cipher = AesCbc::new_from_slices(key.as_bytes(), &bytes[0..16]).unwrap();
    Ok(String::from_utf8(cipher.decrypt_vec(&bytes[16..]).unwrap()).unwrap())
}


pub async fn email_verify(rb: &mut Rbatis, email_str: &str) -> bool {
    let email_query_res = User::select_by_email(rb, email_str.to_string())
        .await;
    match email_query_res {
       Ok(res) => {
           match res {
               Some(res) => {
                   true
               }
               None => {
                   false
               }
           }
       }
        _ => {
            false
        }
    }
}

pub async fn verify(rb: &mut Rbatis, login_user: &str, login_password: &str) -> bool{
    let user_query_res = User::select_by_username(rb, login_user.to_string()).await;
    match user_query_res {
        Ok(res) => {
            match res {
                Some(res) => {
                    let encrypt_key: String = std::env::var("KEY").unwrap_or_else(|_| "01234567012345670123456701234567".to_string());
                    let saved_password = res.password.unwrap();
                    let dencrypt_password = decrypt(&encrypt_key, &saved_password).unwrap();
                    if login_password == dencrypt_password {
                        println!("user {:?} verify successfully", login_user);
                        true
                    } else {
                        println!("user {:?} verify failed", login_user);
                        false
                    }
                }
                None => {
                    println!("user {:?} query failed", login_user);
                    false
                }
            }
        }
        Err(err) => {
            println!("auth query failed: {:?}", err.to_string());
            false
        }
    }
}